﻿using System;
using System.Net;
using System.Reflection;
using System.Text;
using System.Web.Services.Protocols;
using Microsoft.Office.Project.Server.Library;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using MSPE.Public.FOSS.AutoStatusPlus.TimerJob;

namespace MSPE.Public.FOSS.AutoStatusPlus.Common
{
    internal static class Utility
    {
        internal static void AddConfigurationKey(SPWeb pwaWeb, string keyName, string value)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid SPWeb object.");

            SPPropertyBag _pwaWebProperties = pwaWeb.Properties;

            if (_pwaWebProperties == null)
                throw new Exception("Unable to retrieve Site properties from " + pwaWeb.Url);

            if (!_pwaWebProperties.ContainsKey(keyName))
            {
                pwaWeb.AllowUnsafeUpdates = true;
                _pwaWebProperties.Add(keyName, value);
                _pwaWebProperties.Update();
                pwaWeb.Update();
                pwaWeb.AllowUnsafeUpdates = false;
            } // End if
        } // AddConfigurationKey()

        internal static bool AllKeysExist(SPWeb pwaWeb)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid non-null SPWeb object.");

            bool _dbNameKeyExists = KeyExists(pwaWeb, GlobalConstants.DbNameKey);
            bool _dbPasswordKeyExists = KeyExists(pwaWeb, GlobalConstants.DbPasswordKey);
            bool _dbServerKeyExists = KeyExists(pwaWeb, GlobalConstants.DbServerKey);
            bool _dbUseIntegratedAuthKeyExists = KeyExists(pwaWeb, GlobalConstants.DbUseIntegratedAuthKey);
            bool _dbUserKeyExists = KeyExists(pwaWeb, GlobalConstants.DbUserKey);
            bool _logFilePathKeyExists = KeyExists(pwaWeb, GlobalConstants.LogFilePathKey);
            bool _logFilePrefixKeyExists = KeyExists(pwaWeb, GlobalConstants.LogFilePrefixKey);
            bool _logTimesheetEventsKeyExists = KeyExists(pwaWeb, GlobalConstants.LogTimesheetEventsKey);
            bool _logUpdatesKeyExists = KeyExists(pwaWeb, GlobalConstants.LogUpdatesKey);
            bool _newLogFileEveryHourKeyExists = KeyExists(pwaWeb, GlobalConstants.NewLogFileEveryHourKey);
            bool _pollingIntervalKeyExists = KeyExists(pwaWeb, GlobalConstants.PollingIntervalKey);
            bool _processBlankLinesKeyExists = KeyExists(pwaWeb, GlobalConstants.ProcessBlankLinesKey);
            bool _eventKeyExists = KeyExists(pwaWeb, GlobalConstants.EventKey);
            bool _sspHostnameKeyExists = KeyExists(pwaWeb, GlobalConstants.SspHostnameKey);
            bool _sspNameKeyExists = KeyExists(pwaWeb, GlobalConstants.SspNameKey);
            bool _submitUpdatesKeyExists = KeyExists(pwaWeb, GlobalConstants.SubmitUpdatesKey);
            bool _urlKeyExists = KeyExists(pwaWeb, GlobalConstants.UrlKey);
            bool _webConfiguredKeyExists = KeyExists(pwaWeb, GlobalConstants.WebIsConfiguredKey);

            return _dbNameKeyExists && _dbPasswordKeyExists && _dbServerKeyExists && _dbUseIntegratedAuthKeyExists && _dbUserKeyExists && _eventKeyExists
                && _logFilePathKeyExists &&_logFilePrefixKeyExists &&  _logTimesheetEventsKeyExists && _logUpdatesKeyExists && _newLogFileEveryHourKeyExists
                 && _pollingIntervalKeyExists && _processBlankLinesKeyExists && _sspHostnameKeyExists && _sspNameKeyExists && _submitUpdatesKeyExists
                && _urlKeyExists && _webConfiguredKeyExists;
        } // AllKeysExist()

        internal static string BuildConnectionString(bool dbUseIntegratedAuth, string dbServer, string dbName,
            string dbUser, string dbPassword)
        {
            // Create connection string based on SSPI setting
            if (dbUseIntegratedAuth)
                return string.Format("Data Source={0};Initial Catalog={1};Integrated Security=True", dbServer, dbName);

            return string.Format("Data Source={0};Initial Catalog={1};User Id={2};Password={3};",
                                 dbServer, dbName, dbUser, dbPassword);
        } // BuildConnectionString()

        internal static string BuildSspUrl(SPSite site, string hostName, string sspName)
        {
            string _ssHostName = site.HostName;

            if (hostName.Length > 0)
                _ssHostName = hostName;

            if (site.Protocol == "HTTPS:")
                return string.Format("{0}//{1}:56738/{2}", site.Protocol, _ssHostName, sspName);

            return string.Format("{0}//{1}:56737/{2}", site.Protocol, _ssHostName, sspName);
        } // BuildSspUrl()

        internal static string BuildTimerJobName(string url)
        {
            return GlobalConstants.TimerJobBaseName + " - " + url;
        } // BuildTimerJobName()

        internal static void CreateConfigurationKeys(SPWeb pwaWeb)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid non-null SPWeb object.");

            AddConfigurationKey(pwaWeb, GlobalConstants.DbNameKey, GlobalConstants.DbNameInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.DbPasswordKey, GlobalConstants.DbPasswordInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.DbServerKey, GlobalConstants.DbServerInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.DbUseIntegratedAuthKey, GlobalConstants.DbUseIntegratedAuthInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.DbUserKey, GlobalConstants.DbUserInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.EventKey, GlobalConstants.EventInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.ProcessBlankLinesKey, GlobalConstants.ProcessBlankLinesInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.LogFilePathKey, GlobalConstants.LogFilePathInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.LogFilePrefixKey, GlobalConstants.LogFilePrefixInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.LogTimesheetEventsKey, GlobalConstants.LogTimesheetEventsInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.LogUpdatesKey, GlobalConstants.LogUpdatesInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.NewLogFileEveryHourKey, GlobalConstants.NewLogFileEveryHourInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.PollingIntervalKey, GlobalConstants.PollingIntervalInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.SspHostnameKey, GlobalConstants.SspHostnameInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.SspNameKey, GlobalConstants.SspNameInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.SubmitUpdatesKey, GlobalConstants.SubmitUpdatesInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.UrlKey, GlobalConstants.UrlInitialValue);
            AddConfigurationKey(pwaWeb, GlobalConstants.WebIsConfiguredKey, GlobalConstants.WebIsConfiguredInitialValue);
        } // CreateConfigurationKeys()


        internal static int CreateTimerJob(SPWeb pwaWeb, Settings settings)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb");

            if (settings == null)
                throw new ArgumentNullException("settings");

            string _jobName = BuildTimerJobName(pwaWeb.Url);
            int _jobDeleteCount = 0;

            // Delete any existing jobs for this Site's Web Application
            // with the appropriate name
            foreach (SPJobDefinition _job in pwaWeb.Site.WebApplication.JobDefinitions)
            {
                if (_job.Name.ToLower() == _jobName.ToLower())
                {
                    _job.Delete();
                    _jobDeleteCount++;
                } // End if
            } // End foreach
            
            // Create a new job in the PWA site's web application
            StatusingTimerJob _statusingJob = new StatusingTimerJob(_jobName, pwaWeb.Site.WebApplication, SPJobLockType.Job, pwaWeb.Url);

            // Create a schedule using the default parameters
            SPMinuteSchedule _jobSchedule = new SPMinuteSchedule();
            _jobSchedule.BeginSecond = GlobalConstants.TimerJobBeginSecond;
            _jobSchedule.EndSecond = GlobalConstants.TimerJobEndSecond;
            _jobSchedule.Interval = settings.PollingInterval;

            // Configure the job with the schedule and create it
            _statusingJob.Schedule = _jobSchedule;
            _statusingJob.Update();

            return _jobDeleteCount;
        } // CreateTimerJob()

        internal static void DeleteConfigurationKeys(SPWeb pwaWeb)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid SPWeb object.");

            RemoveConfigurationKey(pwaWeb, GlobalConstants.DbNameKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.DbPasswordKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.DbServerKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.DbUseIntegratedAuthKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.DbUserKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.EventKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.ProcessBlankLinesKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.LogFilePathKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.LogFilePrefixKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.LogTimesheetEventsKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.LogUpdatesKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.NewLogFileEveryHourKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.PollingIntervalKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.SspHostnameKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.SspNameKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.SubmitUpdatesKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.UrlKey);
            RemoveConfigurationKey(pwaWeb, GlobalConstants.WebIsConfiguredKey);            
        } // DeleteConfigurationKeys()

        internal static int DeleteTimerJob(SPWeb pwaWeb)
        {
            string _jobName = BuildTimerJobName(pwaWeb.Url);
            int _jobDeleteCount = 0;

            // Delete any existing jobs for this Site's Web Application
            // with the appropriate name
            foreach (SPJobDefinition _job in pwaWeb.Site.WebApplication.JobDefinitions)
            {
                if (_job.Name.ToLower() == _jobName.ToLower())
                {
                    _job.Delete();
                    _jobDeleteCount++;
                } // End if
            } // End foreach

            return _jobDeleteCount;
        } // DeleteTimerJob()

        internal static Exception ExpandPsiException(SoapException ex)
        {
            // Declarations
            StringBuilder _errorMessage = new StringBuilder();
            PSClientError _psiError = new PSClientError(ex);
            PSErrorInfo[] _errors = _psiError.GetAllErrors();
            PSErrorInfo _error;

            if (_errors.Length == 0)
                return ex;

            _errorMessage.AppendLine("".PadRight(30, '='));
            _errorMessage.AppendLine("Error:");
            _errorMessage.AppendLine();

            // Loop through and extract all errors
            for (int i = 0; i < _errors.Length; i++)
            {
                _error = _errors[i];
                _errorMessage.AppendLine(ex.Message);
                _errorMessage.AppendLine("".PadRight(30, '='));
                _errorMessage.AppendLine("PSClientErrorOutput:");
                _errorMessage.AppendLine();
                _errorMessage.AppendLine(_error.ErrId.ToString());
                _errorMessage.AppendLine();

                for (int j = 0; j < _error.ErrorAttributes.Length; j++)
                {
                    _errorMessage.Append("\t");
                    _errorMessage.Append(_error.ErrorAttributeNames()[j]);
                    _errorMessage.Append(": ");
                    _errorMessage.Append(_error.ErrorAttributes[j]);
                }

                _errorMessage.AppendLine();
                _errorMessage.AppendLine("Exception InnerXML:");
                _errorMessage.AppendLine();
                _errorMessage.Append(ex.Detail.InnerXml);
                _errorMessage.Append("\r\n".PadRight(30, '='));
            }

            return new Exception(_errorMessage.ToString(), ex);
        }

        /// <summary>
        /// Format time for log entry
        /// </summary>
        /// <returns>date string</returns>
        internal static string FormatTimeLog(bool byTheHour)
        {
            DateTime _dtNow = DateTime.Now;
            String _year = _dtNow.Year.ToString();
            String _month = FormatTwoDigits(_dtNow.Month);
            String _day = FormatTwoDigits(_dtNow.Day);
            String _hour = FormatTwoDigits(_dtNow.Hour);
            String _minutes = FormatTwoDigits(_dtNow.Minute);
            String _seconds = FormatTwoDigits(_dtNow.Second);

            if (byTheHour)
                return _year + _month + _day + "-" + _hour;
            
            return _year + _month + _day + "-" + _hour + _minutes + _seconds;
        } // FormatTimeLog()

        /// <summary>
        /// Format two digit
        /// </summary>
        /// <param name="source">datetine integer</param>
        /// <returns>datetime string</returns>
        internal static string FormatTwoDigits(int source)
        {
            if (source < 10)
                return String.Format("0{0}", source);
            
            return source.ToString();
        } // FormatTwoDigits()

        
        internal static bool KeyExists(SPWeb pwaWeb, string keyName)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid non-null SPWeb object.");

            SPPropertyBag _pwaWebProperties = pwaWeb.Properties;

            if (_pwaWebProperties == null)
                throw new Exception("Unable to retrieve properties for Site at URL " + pwaWeb.Url + " .");

            return _pwaWebProperties.ContainsKey(keyName);
        } // KeyExists()

        internal static Settings LoadSettings(Guid siteGuid)
        {
            SPSite _pwaSite = null;
            SPWeb _pwaWeb = null;

            try
            {
                _pwaSite = new SPSite(siteGuid);
                _pwaWeb = _pwaSite.RootWeb;

                return Settings.LoadSettings(_pwaWeb);
            } // End try

            finally
            {
                if (_pwaSite != null) _pwaSite.Dispose();
                if (_pwaWeb != null) _pwaWeb.Dispose();
            } // End finally
        } // LoadSettings()

        internal static void RegisterEventHandler(SPWeb pwaWeb, TimesheetEventEnum eventToUpdate)
        {
            //todo: alter to check if someone already has handler position 0
            // Declarations
            Assembly _currentAssembly;
            EventsSvc.Events _eventsSvc = null;
            EventsSvc.EventHandlersDataSet _eventHandlersDs = null;
            EventsSvc.EventHandlersDataSet _eventHandlersUpdateDs = null;
            WebServiceManager _webRefMgr;

            if (!Enum.IsDefined(typeof(TimesheetEventEnum), eventToUpdate))
                throw new Exception(eventToUpdate + " is not a valid TimesheetEvent.");

            try
            {
                // Initializations
                _webRefMgr = new WebServiceManager {PwaUrl = pwaWeb.Url};
                _eventsSvc = _webRefMgr.GetWebRef(WebServiceTypeEnum.Events) as EventsSvc.Events;
                _eventHandlersUpdateDs = new EventsSvc.EventHandlersDataSet();
                _currentAssembly = Assembly.GetExecutingAssembly();

                if (_eventsSvc == null)
                    throw new Exception("Unable to initialize web reference!");
               
                // Get a list of event handlers
                _eventHandlersDs = _eventsSvc.ReadEventHandlerAssociationsForEvent((EventsSvc.PSEventID)eventToUpdate);

                // Make sure the event handler doesn't already exist
                foreach (EventsSvc.EventHandlersDataSet.EventHandlersRow _eventHandler in _eventHandlersDs.EventHandlers)
                {
                    if (_eventHandler.Name == GlobalConstants.EventHandlerName)
                        throw new InvalidOperationException("Event handler already exists.");
                } // End foreach

                string _eventHandlerName = GlobalConstants.EventHandlerName + " - " + eventToUpdate;

                // Set up the event handler association
                _eventHandlersUpdateDs.EventHandlers.AddEventHandlersRow(Guid.NewGuid(),
                                                                        _eventHandlerName,
                                                                         _currentAssembly.FullName,
                                                                         GlobalConstants.EventHandlerClass,
                                                                         (int)eventToUpdate,
                                                                         GlobalConstants.EventHandlerDescription,
                                                                         GlobalConstants.EventHandlerOrder);


                // Associate the event handler
                _eventsSvc.CreateEventHandlerAssociations(_eventHandlersUpdateDs);
            } // End try
            
            finally
            {
                if (_eventsSvc != null) _eventsSvc.Dispose();
                if (_eventHandlersUpdateDs != null) _eventHandlersUpdateDs.Dispose();
                if (_eventHandlersDs != null) _eventHandlersDs.Dispose();
            } // End finally
        } // RegisterEventHandler()

        internal static string ReadConfigurationKey(SPWeb pwaWeb, string keyName)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid SPWeb object.");

            SPPropertyBag _pwaWebProperties = pwaWeb.Properties;

            if (_pwaWebProperties == null)
                throw new Exception("Unable to retrieve Site properties from " + pwaWeb.Url);

            if (_pwaWebProperties.ContainsKey(keyName))
                return _pwaWebProperties[keyName];

            throw new ArgumentException(pwaWeb.Url + " does not contain the key " + keyName + ".");
        } // ReadConfigurationKey()

        internal static void RemoveConfigurationKey(SPWeb pwaWeb, string keyName)
        {
            if (pwaWeb == null)
                throw new ArgumentNullException("pwaWeb", "Caller must provide a valid SPWeb object.");

            SPPropertyBag _pwaWebProperties = pwaWeb.Properties;

            if (_pwaWebProperties == null)
                throw new Exception("Unable to retrieve Site properties from " + pwaWeb.Url);

            if (_pwaWebProperties.ContainsKey(keyName))
            {
                pwaWeb.AllowUnsafeUpdates = true;
                _pwaWebProperties[keyName] = null;
                _pwaWebProperties.Update();
                pwaWeb.Update();
                pwaWeb.AllowUnsafeUpdates = false;
            } // End if
        } // RemoveConfigurationKey

        internal static void UnregisterEventHandler(SPWeb pwaWeb, TimesheetEventEnum eventToUpdate)
        {
            // Declarations
            EventsSvc.Events _eventsSvc = null;
            EventsSvc.EventHandlersDataSet _eventHandlersDs = null;
            Guid[] _eventHandlerGuids;
            WebServiceManager _webRefMgr;

            if (!Enum.IsDefined(typeof(TimesheetEventEnum), eventToUpdate))
                throw new Exception(eventToUpdate + " is not a valid TimesheetEvent.");

            try
            {
                // Initializations
                _eventHandlerGuids = new Guid[1];
                _webRefMgr = new WebServiceManager {PwaUrl = pwaWeb.Url};
                _eventsSvc = _webRefMgr.GetWebRef(WebServiceTypeEnum.Events) as EventsSvc.Events;

                if (_eventsSvc == null)
                    throw new Exception("Unable to initialize web reference!");

                string _eventHandlerName = GlobalConstants.EventHandlerName + " - " + eventToUpdate;

                // Get a list of event handlers
                _eventHandlersDs = _eventsSvc.ReadEventHandlerAssociationsForEvent((EventsSvc.PSEventID)eventToUpdate);

                foreach (EventsSvc.EventHandlersDataSet.EventHandlersRow _eventHandler in _eventHandlersDs.EventHandlers)
                {
                    if (_eventHandler.Name == _eventHandlerName || _eventHandler.Name.Contains(GlobalConstants.EventHandlerName))
                    {                        
                        _eventHandlerGuids[0] = _eventHandler.EventHandlerUid;
                        _eventsSvc.DeleteEventHandlerAssociations(_eventHandlerGuids);
                    } // End if
                } // End foreach
            } // End try
            
            finally
            {
                if (_eventsSvc != null) _eventsSvc.Dispose();
                if (_eventHandlersDs != null) _eventHandlersDs.Dispose();
            } // End finally
        } // UnregisterEventHandler()
    }
}
